class MethodParser extends TreeParser;

options {
	importVocab = Java;
}

{
    public String methodName;
    public String callerName;
    public String callerType;
    public LinkedList parameters;
    public LinkedList parameterTypes = new LinkedList();
    public LinkedList nArray = new LinkedList();

    public String returnType;
    public String assignToVar;
    public AST astList;

    public void addSymbolTable(SymbolTable t) {
        _symbolTables.add(t);
    }

    public void setClassName(String className) {
        _className = className;
    }

    public void setPackage(String pack) {
        _package = pack;
    }

    public void setClassInfo(ClassData ci) {
        _currentCInfo = ci;
    }

    private ClassData _currentCInfo;

    private StringBuffer _currentID;
    private StringBuffer _currentModifiers;
    private StringBuffer _currentEXPR;
    private LinkedList _exprList = new LinkedList();
    private Stack _exprStack = new Stack();
    private Stack _typeStack = new Stack();
    private Stack _nArrayStack = new Stack();
    private int _ntypeArray;
    private String _typeName;
    private String _currentConst;
    private StringBuffer _initializer;
    private StringBuffer _currentArrayDecl;

    private String _className;
    private String _currentEXPRType;
    private LinkedList _symbolTables = new LinkedList();   

    private boolean _parsingMethod = false;
    private boolean _parsingMethodCaller = false;

    private String _package;
    
    private int _narray = 0;
    private int _narrayOld;
    
    private LinkedList _newparameterTypes;
    private LinkedList _newnArray;
    
    /* Atualiza um valor existente do currentEXPRType a partir de um nome nao composto */
    private void updateCurrentType(String tk) {
        if(_currentEXPRType == null) return;
	
	String type = _currentEXPRType.toString();
        if(type != null) {
  	    boolean loaded = false;
 	    Class c = Util.getType(type, _className, _package, _currentCInfo.getImports());
	    
	    // Se o tipo atual for um array, o tk so pode ser length ou algum metodo de object 
	    if(c.isArray()) {
                if(tk.equals("length")) {
		    _currentEXPRType = new String("int");
		}
		else {
		    System.out.println("MethodParser.updateCurrentType : array case -> unkown " + tk);
		}
		return;
	    }

            // Verifica o caso de tk ser um campo da classe c
 	    try {
                Field f = c.getField(tk);
		c = f.getType();
	        _currentEXPRType = c.getName();

/*		_narray = 0;
		while(c.isArray()) {
		    _narray++;
                    c = c.getComponentType();
                } */
		
		loaded = true;		
	    }
	    catch(NoSuchFieldException ex) {
                _currentEXPRType = type;
            }
	    
            if(!loaded) {
                try {
		    Field f = c.getDeclaredField(tk);
                    c = f.getType();
		    _currentEXPRType = c.getName();
		    
/*		    _narray = 0;
		    while(c.isArray()) {
                        _narray++;
			c = c.getComponentType();
		    } */

                    loaded = true;
		}
		catch(NoSuchFieldException ex) {
		    _currentEXPRType = type;
	        }
	    }
	    
	    // Verifica o caso que tk e uma class interna
	    if(!loaded) {
	        String tname = new String(_currentEXPRType + "$" + tk);
	        Class [] classes = c.getDeclaredClasses();
		for(int i = 0;i < classes.length;i++) {
		    if(classes[i].getName().equals(tname)) {
		        _currentEXPRType = tname;
			loaded = true;
			break;		    
		    }
	        }
	    }

            if(!loaded) {
                System.out.println(" %%%% MethodParser ERROR : nao carreguei o field " + 
		    tk +  " a partir de  " + _currentEXPRType.toString());
	    }
	}
    }
}

parsemc:      { returnType = null; assignToVar = null; }
              #(CTOR_CALL {
                       parameters = new LinkedList(); 
                       methodName = null;
    
                       Class c = null;
                       if(_currentCInfo.isInner()) {
                           c = Util.getType(Util.innerClassLoader(_className), null,
                               _package, _currentCInfo.getImports());
                       }
                       else {
                           c = Util.getType(_className, null, _package, _currentCInfo.getImports());
                       }
    
                       callerType = c.getName();
                       callerName = new String("this");
                  }

	         elist {
		     for(int i = 0;i < _exprList.size();i++) {
	                 parameters.add((String) _exprList.get(i));
	             }
		 } )

	    | #( SUPER_CTOR_CALL { parameters = new LinkedList(); methodName = new String("super"); }
			(	
                        {
                            Class c = null;
                            if(_currentCInfo.isInner()) {
                                c = Util.getType(Util.innerClassLoader(_className), null,
                                    _package, _currentCInfo.getImports());
                            }
                            else {
                                c = Util.getType(_className, null, _package, _currentCInfo.getImports());
                            }
                            c = c.getSuperclass();

                            callerType = c.getName();
                            callerName = new String("super");
                            methodName = null;
                        }

                        elist {
                            for(int i = 0;i < _exprList.size();i++) {
                                parameters.add((String) _exprList.get(i));					
                            }
                        }

			|	primaryExpression { System.out.println("METHOD PARSER!!!"); } elist
			
			)
		 ) 
		
	    | #(VARIABLE_DEF 
	          modifiers 
		  typeSpec
		
		  variableDeclarator

                  #(ASSIGN 
		     #(EXPR (
		        
		           mcall 
		       | 
		    
		         { _parsingMethod = true; } 
			 
			   newExpression {
		               methodName = null;
                               callerName = null;
                               callerType = null;

                               parameters = null;
			   
                               parameterTypes = _newparameterTypes;
                               nArray = _newnArray;
                               assignToVar = null;
			       
			       
			       // Pode ser a declaracao de uma nova classe interna
			       
			       /*
			       StringTokenizer stk = new StringTokenizer(_currentEXPRType, ".");
			       if(stk.countTokens() > 1) {
			           StringBuffer buf = new StringBuffer();
				   int count = stk.countTokens() - 1;
				   for(int k = 0;k < count;k++) {
				       buf.append(stk.nextToken() + "$");
				   }
				   buf.append(stk.nextToken());
				   _currentEXPRType = buf.toString();
			       } */
			       
			       
                               returnType = _currentEXPRType;
			       
			       
		           }
		   )))
		  )

	    | #(EXPR #(ASSIGN 

		  expr2

		  mcall

		  ) )
		  
 	    | methodCall
	    
	;

mcall:
          tcast
	  
	  | methodCall
	  
	;

tcast: 
	#(TYPECAST { StringBuffer ret = new StringBuffer("(("); }
	     typeSpec {
		 ret.append(_typeName + " ");
		 for(int i = 0;i < _ntypeArray;i++) {
		     ret.append("[]");
		 }
	         ret.append(")");
	         returnType = _typeName;
              }
	      methodCall
	  )
	;

// currentEXPRType acumula o nome do tipo do caller
// currentEXPR a expressao do caller = variavel + metodo.
// exprList a lista de expressoes dos parametros
methodCall: #(n1:METHOD_CALL { parameters = new LinkedList(); 
                               _currentEXPRType = null; 
			       _parsingMethod = true;
			       _parsingMethodCaller = true; }

	             primaryExpression {

			StringTokenizer st = new StringTokenizer(_currentEXPR.toString(), ".");
			int index = st.countTokens();
			if(index == 1) {
			    callerName = new String("this");
			    methodName = _currentEXPR.toString();
			    if(_currentEXPRType == null) {
			        callerType = _className;  // .... o className
			    }
			    else {
			        callerType = _currentEXPRType.toString();
			    }
			}
			else {
			    StringBuffer buf = new StringBuffer();
			    for(int i = 0;i < index - 1;i++) {
			        String tk = st.nextToken();
			        buf.append(tk);
				if(i < index - 2) buf.append(".");

				if(i == 0) continue; // eu ja carreguei o primeiro currentEXPRType no IDENT

				updateCurrentType(tk);
			    }
			    callerName = buf.toString();
			    methodName = st.nextToken();

			    // Metodo static
			    if(_currentEXPRType == null) {
			        Class c = Util.getType(callerName, null,
                                    _package, _currentCInfo.getImports());
				callerType = c.getName();
			    }
			    else {
			        callerType = _currentEXPRType.toString();
			    }
			}
			_parsingMethodCaller = false;
		     }

		     el:elist {
		         for(int i = 0;i < _exprList.size();i++) {
			     parameters.add((String) _exprList.get(i));
			 }
			 astList = el;
		     } )
	;

variableDeclarator
	:	n1:IDENT { assignToVar = n1.getText(); }
	|	LBRACK variableDeclarator
	;

methodInitializer
	:	#(ASSIGN initializer)
	|
	;

elist        { _exprStack.push(_exprList); _exprList = new LinkedList(); }
	:	#( ELIST (expression { _exprList.add(_currentEXPR.toString());
				       parameterTypes.add(_currentEXPRType);
				       nArray.add(new Integer(_narray));
				       } )* )
	;

expression
	:	#(EXPR expr)
	;


expr:  { String type1, type2; }
	        #(QUESTION expr { String _exp1 = _currentEXPR.toString(); }  
		           expr { String _exp2 = _currentEXPR.toString(); type1 = _currentEXPRType; } 
			   expr { String _exp3 = _currentEXPR.toString(); type2 = _currentEXPRType; 
			       
 			       _currentEXPR = new StringBuffer("(" + _exp1 + ")?" + _exp2 + ":" + _exp3); 
			       
			       /*
			       if(!type1.equals(type2)) {
			           if((type1.equals("short") && type2.equals("byte")) ||
				      (type1.equals("byte") && type2.equals("short")) {
				       _currentEXPRType = new String("short");  
				   }
				   else {
				   
				   }
			       }*/

			   } )

	|	#(ASSIGN expr { String _exp1 = _currentEXPR.toString(); } 
	                 expr { String _exp2 = _currentEXPR.toString();
                               _currentEXPR = new StringBuffer(_exp1 + " = " + _exp2); 
			 } )

	|	#(PLUS_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " += " + _exp2);
				  
			      } )

	|	#(MINUS_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			       expr { String _exp2 = _currentEXPR.toString();
			          _currentEXPR = new StringBuffer(_exp1 + " -= " + _exp2);
		 	       } )

	|	#(STAR_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " *= " + _exp2);
			      } )

	|	#(DIV_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			     expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " /= " + _exp2);
			     } )

	|	#(MOD_ASSIGN  expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString();
			          _currentEXPR = new StringBuffer(_exp1 + " %= " + _exp2);
			      } )

	|	#(SR_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			    expr { String _exp2 = _currentEXPR.toString();
			          _currentEXPR = new StringBuffer(_exp1 + " >>= " + _exp2);
			    } )

	|	#(BSR_ASSIGN  expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " >>>= " + _exp2);
			      } )

	|	#(SL_ASSIGN   expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " <<= " + _exp2);
			      } )

	|	#(BAND_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " &= " + _exp2);
			      } )

	|	#(BXOR_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " ^= " + _exp2);
			      } )

	|	#(BOR_ASSIGN  expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " |= " + _exp2);
			      } )

	|	#(LOR expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " || " + _exp2 + ")");
				  _currentEXPRType = new String("boolean");
			      } )

	|	#(LAND expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " && " + _exp2 + ")");
				  _currentEXPRType = new String("boolean");
			      } )

	|	#(BOR expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " | " + _exp2 + ")");
			      } )

	|	#(BXOR expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " ^ " + _exp2 + ")");
			      } )

	|	#(BAND expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " & " + _exp2 + ")");
			      } )

	|	#(NOT_EQUAL expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " != " + _exp2 + ")");
				  _currentEXPRType = new String("boolean");
			      } )

	|	#(EQUAL expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " == " + _exp2 + ")");
				  _currentEXPRType = new String("boolean");
			      } )

	|	#(LT expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " < " + _exp2 + ")");
				  _currentEXPRType = new String("boolean");
			      } )

	|	#(GT expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " > " + _exp2 + ")");
				  _currentEXPRType = new String("boolean");
			      } )

	|	#(LE expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " <= " + _exp2 + ")");
 			          _currentEXPRType = new String("boolean");
			      } )

	|	#(GE expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " >= " + _exp2 + ")");
				  _currentEXPRType = new String("boolean");
			      } )

	|	#(SL expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " << " + _exp2 + ")");
			      } )

	|	#(SR expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " >> " + _exp2 + ")");
			      } )

	|	#(BSR expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " >>> " + _exp2 + ")");
			      } )

	|	#(PLUS expr { 
	                      String _exp1 = _currentEXPR.toString(); 
	                      String type1 = _currentEXPRType;
			    }
			      
		       expr { 
			      String _exp2 = _currentEXPR.toString(); 
			      _currentEXPR = new StringBuffer("(" + _exp1 + " + " + _exp2 + ")");
                              String type2 = _currentEXPRType;				  

			      if(type1.equals("String") || type2.equals("String")) {
			          _currentEXPRType = new String("String");
		              }
		       } )

	|	#(MINUS expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " - " + _exp2 + ")");
			      } )

	|	#(DIV expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " / " + _exp2 + ")");
			      } )

	|	#(MOD expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " % " + _exp2 + ")");
			      
			      } )

	|	#(STAR expr { String _exp1 = _currentEXPR.toString(); }
		       expr { String _exp2 = _currentEXPR.toString(); 
			      _currentEXPR = new StringBuffer("(" + _exp1 + " * " + _exp2 + ")");
			    } )
	
	|	#(INC expr { String _exp1 = _currentEXPR.toString(); 
	                     _currentEXPR = new StringBuffer("++" + _exp1); 
			   })

	|	#(DEC expr { String _exp1 = _currentEXPR.toString(); 
	                     _currentEXPR = new StringBuffer("--" + _exp1); } )

	|	#(POST_INC expr { String _exp1 = _currentEXPR.toString(); 
	                          _currentEXPR = new StringBuffer(_exp1 + "++"); } )

	|	#(POST_DEC expr { String _exp1 = _currentEXPR.toString(); 
	                          _currentEXPR = new StringBuffer(_exp1 + "--"); } )

	|	#(BNOT expr { String _exp1 = _currentEXPR.toString(); 
	                      _currentEXPR = new StringBuffer("(~" + _exp1 + ")"); } )

	|	#(LNOT expr { String _exp1 = _currentEXPR.toString(); 
	                      _currentEXPR = new StringBuffer("(!" + _exp1 + ")"); 
			    } )

	|	#("instanceof" expr { String _exp1 = _currentEXPR.toString(); }
			       expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " instanceof " + _exp2 + ")");
				  _currentEXPRType = new String("boolean");
			       } )

	|	#(UNARY_MINUS expr { String _exp1 = _currentEXPR.toString(); 
	                             _currentEXPR = new StringBuffer("-" + _exp1); } )

	|	#(UNARY_PLUS expr { String _exp1 = _currentEXPR.toString(); 
	                            _currentEXPR = new StringBuffer("+" + _exp1); } )

	|	primaryExpression

	;

primaryExpression

    :   n1:IDENT {
            _currentEXPR = new StringBuffer(n1.getText());
            if(_parsingMethod) {
                boolean resolved = false;
		// E um identificador ?
	        for(int i = 0;i < _symbolTables.size();i++) {
	            SymbolTable s = (SymbolTable) _symbolTables.get(i);
		    STEntry e = (STEntry) s.find(n1.getText());
		    if(e != null) {
			_narray = e.getNarray();

                        // se for array, tem que criar a instancia
			int count = 0;
			Class comp = e.getType();
			while(count < _narray) {
			    Object obj = Array.newInstance(comp, 0);
			    comp = obj.getClass();
			    count++;
			}
			_currentEXPRType = comp.getName();

		        resolved = true;
		        break;
	 	    }
	        }
	        // E um tipo?
	        if(!resolved) {
	            Class c = Util.getType(n1.getText(), _className,
                        _package, _currentCInfo.getImports());
		    if(c != null) {
		        resolved = true;
			//??
			_narray = 0;
		        _currentEXPRType = c.getName();
		    }
	        }
	        // E this ou super?
	        if(!resolved) {
	            if(n1.getText().equals("super")) {
		        Class c = Util.getType(_className, null, _package, _currentCInfo.getImports());
		        Class sup = c.getSuperclass();
		        _currentEXPRType = sup.getName();
		    }
		    else
		    if(n1.getText().equals("this")) {
	                _currentEXPRType = _className;
		    }
		    _narray = 0;
	        }
		// o identificador deve fazer parte de uma chamada static
		if(!resolved) {
		    _currentEXPRType = null;
		}
	    }
	}

    |   #(	DOT { String dot; String type; }
    
                    (	expr  { dot = _currentEXPR.toString(); type = _currentEXPRType; }

                            (	n2:IDENT {
                                       _currentEXPR = new StringBuffer(dot + "." + n2.getText()); 

                                       // Isso aqui e porque quando eu determino o tipo do caller,
                                       // nao posso incluir o ultimo nome
                                       if(!_parsingMethodCaller) {
                                           updateCurrentType(n2.getText());
                                       }
                                    }

                            |	arrayIndex  { System.out.println("methodparser primaryExpr arrayIndex!!!"); }

                            |	"this"  { 
                                       _currentEXPR = new StringBuffer(dot + ".this");
                                       
                                       // Nao precisa do updateType, pois o ultimo nome e o tipo.
                                       // System.out.println("methodparser primaryExpr this!!!");
                                    }

                            |	"class"  { 
                                        _currentEXPR = new StringBuffer(dot + "." + "class");
                                        if(!_parsingMethodCaller) {					        
                                            _currentEXPRType = new String("java.lang.Class");
                                        }
                            
                                    }

                            |	#( "new" IDENT elist ) {  System.out.println("methodparser primaryExpr newIDENT!!!"); }

                            |   "super"  { System.out.println("methodparser primaryExpr super"); }

                            )
                    |	#(ARRAY_DECLARATOR typeSpecArray)  { System.out.println("methodparser primaryExpr arraydecla!!"); }
                    
                    |	builtInType ("class")? { System.out.println("methodparser primaryExpr builtIntype!!!"); }

                    )
		)

	|	arrayIndex 

	|	#(nx:METHOD_CALL 
	             primaryExpression 
		     elist { System.out.println("TEM METHOD CALL COMO PARAMETRO!!!!! -> " + nx.toStringTree() +
		                 " \ncinfo = " + _currentCInfo.getName() ); } )

	|	{ String t; int na; }
		#(TYPECAST { StringBuffer ret = new StringBuffer("(("); }
	             typeSpec {
		         ret.append(_typeName + " ");
			 na = _ntypeArray;
		         for(int i = 0;i < _ntypeArray;i++) {
		             ret.append("[]");
		         }
			 ret.append(")");
			 t = _typeName;
		     }
		     
		     expr { 
		         ret.append(_currentEXPR.toString() + ")"); 
		         _currentEXPR = new StringBuffer(ret.toString());
			 _currentEXPRType = t;
			 _narray = na;
	             } )

	|   newExpression 

	|   constant 	{ _currentEXPR = new StringBuffer(_currentConst); _narray = 0; }

        |   "super" 	{ 
	                    _currentEXPR = new StringBuffer("super"); 
			    Class t = Util.getType(_className, null, _package, _currentCInfo.getImports());
			    t = t.getSuperclass();
			    _currentEXPRType = t.getName();	
			    _narray = 0;
	                }
			
        |   "true" 	{ _currentEXPR = new StringBuffer("true");  
	                  _currentEXPRType = new String("boolean"); 
			  _narray = 0; }
			  
        |   "false" 	{ _currentEXPR = new StringBuffer("false"); 
	                  _currentEXPRType = new String("boolean");
			  _narray = 0; }
			  
        |   "this" 	{ _currentEXPR = new StringBuffer("this"); 
	                  _currentEXPRType = _className;
			  _narray = 0; }

        |   "null" 	{ _currentEXPR = new StringBuffer("null"); 
	                   _currentEXPRType = new String("null");
			   _narray = 0; }

        |	typeSpec // type name used with instanceof
	;


arrayIndex
	:	#(INDEX_OP { StringBuffer _arrayIndexing = new StringBuffer(); String type; }
	              primaryExpression {
		          _arrayIndexing.append(_currentEXPR.toString());
			  type = _currentEXPRType;
		      }
		      expression { 
		          _arrayIndexing.append("[" + _currentEXPR.toString() + "]");
			  _currentEXPR = _arrayIndexing;

			  _currentEXPRType = type;
			  if(_currentEXPRType != null) {
			      Class c = Util.getType(_currentEXPRType, _className, _package, _currentCInfo.getImports());
			      if(!c.isArray()) {
			          System.out.println("ArrayIndex: type not array " + 
			              _currentEXPRType);
			      }
			      else {
			          _currentEXPRType = c.getComponentType().getName();
			      }
	                  }
		      } )
	;


initializer    { _currentEXPR = new StringBuffer(); }
	:	expression { _initializer.append(_currentEXPR.toString()); }
	|	arrayInitializer
	;

arrayInitializer
	:	#(ARRAY_INIT { _initializer.append("{"); } 
	           initializer ({_initializer.append(","); } initializer)* { _initializer.append("}"); }
		  )
	;

constant
    :   n1:NUM_INT 		{ _currentConst = n1.getText(); _currentEXPRType = new String("int"); }
    |   n2:CHAR_LITERAL	        { _currentConst = n2.getText(); _currentEXPRType = new String("char");  }
    |   n3:STRING_LITERAL	{ _currentConst = n3.getText(); _currentEXPRType = new String("String"); }
    |   n4:NUM_FLOAT		{ _currentConst = n4.getText(); _currentEXPRType = new String("float"); }
    |   n5:NUM_DOUBLE		{ _currentConst = n5.getText(); _currentEXPRType = new String("double"); }
    |   n6:NUM_LONG		{ _currentConst = n6.getText(); _currentEXPRType = new String("long"); }
    ;

newExpression   { StringBuffer ret = new StringBuffer("new "); String type; }
	:	#(	"new" type { ret.append(_typeName + "("); type = _currentEXPRType; }
	
			(	{ _currentArrayDecl = new StringBuffer(); } 
			        newArrayDeclarator {
				    ret.append(_currentArrayDecl.toString());
			        }

				(arrayInitializer { ret.append("= " + _initializer.toString());	} )?

				{ 
				  ret.append(")"); 
				  _currentEXPR = new StringBuffer(ret.toString());
				  _currentEXPRType = type; 
				}
			
			|	{ 
			          _typeStack.push(parameterTypes); 
				  parameterTypes = new LinkedList();
				  _nArrayStack.push(nArray);
				  nArray = new LinkedList(); 
				  _narrayOld = _narray; 
				}

			        elist  {         // (objBlock)?
                                    if(_exprList.size() > 0) {
 		                       for(int i = 0;i < _exprList.size() - 1;i++) {
			                   ret.append((String) _exprList.get(i) + ",");
			                }
			                ret.append((String) _exprList.get(_exprList.size() - 1));
			            }
			            ret.append(")");
			            _currentEXPR = new StringBuffer(ret.toString());

                                    _exprList = (LinkedList) _exprStack.pop();
				    
				    // Um jeito simples se salvar os dados quando processo um
				    // new Type() para construtor.
				    
				    _newparameterTypes = parameterTypes;
				    _newnArray = nArray;

				    parameterTypes = (LinkedList) _typeStack.pop();
				    nArray = (LinkedList) _nArrayStack.pop();
				    _narray = _narrayOld;

				    _currentEXPRType = type;
			        }
			)
		)
	;

newArrayDeclarator  { boolean hasEXPR = false; }
	:	#( ARRAY_DECLARATOR 

		       (newArrayDeclarator)? 

		       ( expression {		       
			    _currentArrayDecl.append("[" + _currentEXPR.toString() + "]");
                            hasEXPR = true; 
		        })?
		       
		        {
		            if(!hasEXPR) {
			       _currentArrayDecl.append("[]");			   
			    }
		        }
		 )
	;

typeSpec        { _ntypeArray = 0; }
	:	#(TYPE typeSpecArray)
	;

typeSpecArray
	:	#( ARRAY_DECLARATOR { _ntypeArray++; } typeSpecArray )
	|	type
	;

type:	identifier 		{ _typeName = _currentID.toString(); 
			          _currentEXPRType = new String(_typeName); }
	|	n1:builtInType	{ _typeName = n1.getText(); }
	;

builtInType
    :   "void"      { _currentEXPRType = new String("void"); }
    |   "boolean"   { _currentEXPRType = new String("boolean");  }
    |   "byte"      { _currentEXPRType = new String("byte");  }
    |   "char"      { _currentEXPRType = new String("char");  }
    |   "short"     { _currentEXPRType = new String("short"); }
    |   "int"       { _currentEXPRType = new String("int"); }
    |   "float"     { _currentEXPRType = new String("float"); }
    |   "long"      { _currentEXPRType = new String("long"); }
    |   "double"    { _currentEXPRType = new String("double"); }
    ;


modifiers   { _currentModifiers = new StringBuffer(); }
	:	#( MODIFIERS (n1:modifier { _currentModifiers.append(n1.getText() + " "); } )* )    
	;


modifier
    :   "private"
    |   "public"
    |   "protected"
    |   "static"
    |   "transient"
    |   "final"
    |   "abstract"
    |   "native"
    |   "threadsafe"
    |   "synchronized"
    |   "const"
    |   "volatile"
	|	"strictfp"
    ;


identifier    { _currentID = new StringBuffer(); } 
	:	x:IDENT  		    { _currentID.append(x.getText()); }
	|	#( DOT identifier y:IDENT ) { _currentID.append("." + y.getText()); }
	;


identifierStar	{ _currentID = new StringBuffer(); } 
	:	x:IDENT                       	 { _currentID.append(x.getText()); }
	|	#( DOT identifier (y:STAR { _currentID.append(".*"); } | y2:IDENT { _currentID.append("." + y2.getText()); } ) )
	;


//---------------------------------------------------------------------------------------------------

// Essa gramatica duplicada e a solucao temporaria para a analise do que vem antes do method call. P.ex.,
// no caso de var[x + y] = ... o tipo do x e y da null devido ao parsingmethod == null. Talvez tenha
// que separa em dois parsers.

elist2
	:	#( ELIST (expression2)* )
	;
expression2
	:	#(EXPR expr2)
	;

expr2:	#(QUESTION expr2 expr2 expr2)	// trinary operator
	|	#(ASSIGN expr2 expr2)			// binary operators...
	|	#(PLUS_ASSIGN expr2 expr2)
	|	#(MINUS_ASSIGN expr2 expr2)
	|	#(STAR_ASSIGN expr2 expr2)
	|	#(DIV_ASSIGN expr2 expr2)
	|	#(MOD_ASSIGN expr2 expr2)
	|	#(SR_ASSIGN expr2 expr2)
	|	#(BSR_ASSIGN expr2 expr2)
	|	#(SL_ASSIGN expr2 expr)
	|	#(BAND_ASSIGN expr2 expr2)
	|	#(BXOR_ASSIGN expr2 expr2)
	|	#(BOR_ASSIGN expr2 expr)
	|	#(LOR expr2 expr2)
	|	#(LAND expr2 expr2)
	|	#(BOR expr2 expr2)
	|	#(BXOR expr2 expr2)
	|	#(BAND expr2 expr2)
	|	#(NOT_EQUAL expr2 expr2)
	|	#(EQUAL expr2 expr2)
	|	#(LT expr expr2)
	|	#(GT expr expr2)
	|	#(LE expr2 expr2)
	|	#(GE expr2 expr2)
	|	#(SL expr2 expr2)
	|	#(SR expr2 expr2)
	|	#(BSR expr2 expr2)
	|	#(PLUS expr2 expr2)
	|	#(MINUS expr2 expr2)
	|	#(DIV expr2 expr2)
	|	#(MOD expr2 expr2)
	|	#(STAR expr2 expr2)
	|	#(INC expr2)
	|	#(DEC expr2)
	|	#(POST_INC expr2)
	|	#(POST_DEC expr2)
	|	#(BNOT expr2)
	|	#(LNOT expr2)
	|	#("instanceof" expr2 expr2)
	|	#(UNARY_MINUS expr2)
	|	#(UNARY_PLUS expr2)
	|	primaryExpression2
	;

primaryExpression2
    :   IDENT
    |   #(	DOT
			(	expr2
				(	IDENT
				|	arrayIndex2
				|	"this"
				|	"class"
				|	#( "new" IDENT elist2 )
				|   "super"
				)
			|	#(ARRAY_DECLARATOR typeSpecArray2)
			|	builtInType2 ("class")?
			)
		)
	|	arrayIndex2
	|	#(METHOD_CALL primaryExpression2 elist2)
	|	#(TYPECAST typeSpec2 expr2)
	|   newExpression2
	|   constant2
    |   "super"
    |   "true"
    |   "false"
    |   "this"
    |   "null"
	|	typeSpec // type name used with instanceof
	;

arrayIndex2
	:	#(INDEX_OP primaryExpression2 expression2)
	;

typeSpec2
	:	#(TYPE typeSpecArray2)
	;
	
typeSpecArray2
	:	#( ARRAY_DECLARATOR typeSpecArray2 )
	|	type2
	;

type2:	identifier2
	|	builtInType2
	;

identifier2   
	:	IDENT  
	|	#( DOT identifier2 IDENT ) 
	;

builtInType2
    :   "void"
    |   "boolean"
    |   "byte"
    |   "char"
    |   "short"
    |   "int"
    |   "float"
    |   "long"
    |   "double"
    ;
    
constant2
    :   NUM_INT
    |   CHAR_LITERAL
    |   STRING_LITERAL
    |   NUM_FLOAT
    |   NUM_DOUBLE
    |   NUM_LONG
    ;

newExpression2
	:	#(	"new" type2
			(	newArrayDeclarator2 //(arrayInitializer2)?
			|	elist2 //(objBlock)?
			)
		)
			
	;

newArrayDeclarator2
	:	#( ARRAY_DECLARATOR (newArrayDeclarator2)? (expression2)? )
	;
    
